package com.example.dynamicjavacode.util;

import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 动态编译java源码类
 *
 * @author rongdi
 * @date 2021-01-06
 */
public class DynamicCompiler {

    private static String classpath;
    private static URLClassLoader classLoader;

    /**
     * 编译指定java源代码
     *
     * @param javaSrc java源代码
     * @return 返回类的全限定名和编译后的class字节码字节数组的映射
     */
    public static Map<String, byte[]> compile(String javaSrc) {
        Pattern packageName = Pattern.compile("package\\s+(\\w+.+\\w)");
        Pattern javaName = Pattern.compile("public\\s+class\\s+(\\w+)");
        Matcher javaNameMatcher = javaName.matcher(javaSrc);
        Matcher packageNameMatcher = packageName.matcher(javaSrc);
        if (javaNameMatcher.find()) {
            String packageStr = packageNameMatcher.find() ? packageNameMatcher.group(1) + "." : "";
            return compile(packageStr + javaNameMatcher.group(1), javaSrc);//+ ".java"
        }
        return null;
    }

    /**
     * 编译指定java源代码
     *
     * @param javaName java文件名
     * @param javaSrc  java源码内容
     * @return 返回类的全限定名和编译后的class字节码字节数组的映射
     */
    public static Map<String, byte[]> compile(String javaName, String javaSrc) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        try (MemoryJavaFileManager manager = new MemoryJavaFileManager(compiler.getStandardFileManager(diagnostics, null, null))) {
            JavaFileObject jfiles = manager.makeStringSource(javaName, javaSrc);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics,
                    Arrays.asList("-encoding", "UTF-8"), null, Arrays.asList(jfiles));
            if (task.call()) {
                return manager.getClassBytes();
            } else {
                for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
                    String error = compileError(diagnostic);
                    throw new RuntimeException(error);
                }
                throw new RuntimeException("compile error");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void  init(){
        classLoader = ((URLClassLoader) DynamicCompiler.class.getClassLoader());
    }
    private static String buildClasspath() {

        StringBuilder sb = new StringBuilder();
        for (URL url : classLoader.getURLs()) {
            String f = url.getFile();
            sb.append(f).append(File.pathSeparator);
        }
        return  sb.toString();
    }

    private static String compileError(Diagnostic diagnostic) {
        StringBuilder res = new StringBuilder();
        res.append("LineNumber:[").append(diagnostic.getLineNumber()).append("]\n");
        res.append("ColumnNumber:[").append(diagnostic.getColumnNumber()).append("]\n");
        res.append("Message:[").append(diagnostic.getMessage(null)).append("]\n");
        return res.toString();
    }

}
